/*
@2024-11-20: 将PL_isMaster功能集成到
@2024-10-23: 基于单芯片相机的socket服务端程序多线程版适配修改后用于2X2拼接相机。该服务端程序运行于4个zynq
             ps端，作为后台程序1

*/

#include "MephistoCAM.h"
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <arpa/inet.h>      // for inet_aton()
#include <netinet/in.h>     // for struct sockaddr_in
#include <stdlib.h>         // for atoi(), strtoll(), strtod()
#include <string.h>
#include <errno.h>
/*#include <sys/time.h>*/
char timestamp[26];
void gen_timestamp(char gmt_str[26]){
    struct timeval tv;    
    struct tm* st;
    gettimeofday(&tv, NULL);    
    
    st = gmtime(&tv.tv_sec);
    // sprintf(gmt_str, "cl%4d-%02d-%02dT%02d:%02d:%06.3f",st->tm_year+1900, st->tm_mon, st->tm_mday, st->tm_hour,st->tm_min,st->tm_sec+tv.tv_usec*1e-6);
    sprintf(gmt_str, "%4d-%02d-%02dT%02d:%02d:%06.3f",st->tm_year+1900, st->tm_mon+1, st->tm_mday, st->tm_hour,st->tm_min,st->tm_sec+tv.tv_usec*1e-6);
}


char myName[64];        // 保存每个CCD的名称Blue1, Blue2, Blue3, Blue4
uint32_t isMaster = 0;  // 默认全部设为0，即slave模式
char binFilename[512];  // 保存输出bin文件的名字

char logFilename[1024]; // 
FILE *fp_log;

static char g_done_str[64] = {0};


uint32_t cStatus = 0;   // 记录当前的工作状态，有且仅有两种可能得状态：
                        // 0: 表示空闲，没有曝光或者读出
                        // 1: 表示接收到ICS的拍摄指令了，正在执行中

int ccd_readout_fpXX( uint32_t *gpio_reg_ctrl, 
                     uint32_t *gpio_reg_status, 
                     uint32_t n_row,
                     uint32_t n_col,
                     uint32_t n_bytes,    // 2 for 16bits unsigned; 4 for 32bits float
                     char *filename );

void *serverForClient(void * arg);
void abortExposure(void *arg);

static int global_abort_exposure = 0;

// —————— 新增：全局配置结构 ——————
typedef struct {
    int   chip_id;
    char  ics_ip[16];
    int   ics_port;
    char  ps_ip[16];
    int   ps_port;
    
} config_t;
static config_t g_cfg;



// —————— 新增：从 ics.ini 中读取配置 ——————
static int read_config(const char* path, config_t * cfg) {
    FILE * f = fopen(path, "r");
    if (!f) return -1;
    char line[256];
    while (fgets(line, sizeof(line), f)) {
               // 跳过空行和注释
            if (line[0] == '#' || line[0] == '\n') continue;
               // 去掉末尾的换行
            line[strcspn(line, "\r\n")] = '\0';
        char* eq = strchr(line, '=');
        if (!eq) continue;
        *eq = '\0';
        char* key = line;
        char* val = eq + 1;
        if (strcmp(key, "chip_id") == 0) {
            cfg->chip_id = atoi(val);
            
        }
        else if (strcmp(key, "ics_ip") == 0) {
            strncpy(cfg->ics_ip, val, sizeof(cfg->ics_ip) - 1);
            cfg->ics_ip[sizeof(cfg->ics_ip) - 1] = '\0';
            
        }
        else if (strcmp(key, "ics_port") == 0) {
            cfg->ics_port = atoi(val);
            
        }
        
    }
    fclose(f);
    return 0;
    
}

static void send_done_message(void) {
    fprintf(stderr, "udp%s", g_done_str);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket for done_msg");
        return;
    }

    struct sockaddr_in dst = { 0 };
    dst.sin_family = AF_INET;
    dst.sin_port = htons(g_cfg.ics_port);
    if (inet_aton(g_cfg.ics_ip, &dst.sin_addr) == 0) {
        fprintf(stderr, "invalid ics_ip: %s\n", g_cfg.ics_ip);
        close(sock);
        return;
    }

    char msg[64];
    int len = snprintf(msg, sizeof(msg),
        "get_frame_290:done=%s",
        g_done_str);
    if (len < 0 || len >= (int)sizeof(msg)) {
        close(sock);
        return;
    }

    // ====== 新增：发送 + 重试机制 ======
    const int max_retries = 3;    // 最多重试次数
    const int retry_delay_ms = 200; // 重试间隔 (毫秒)
    int attempt;
    for (attempt = 1; attempt <= max_retries; attempt++) {
        ssize_t ret = sendto(sock, msg, len, 0,
            (struct sockaddr*)&dst, sizeof(dst));
        if (ret < 0) {
            perror("sendto failed");
            if (attempt < max_retries) {
                fprintf(stderr, "Retrying send (%d/%d)...\n", attempt, max_retries);
                fprintf(fp_log, "Retrying send (%d/%d)...\n", attempt, max_retries);
                fflush(fp_log);
                struct timespec ts;
                ts.tv_sec = retry_delay_ms / 1000;
                ts.tv_nsec = (retry_delay_ms % 1000) * 1000000L;
                nanosleep(&ts, NULL);  // 等待指定毫秒再重试
                continue;
            }
            else {
                fprintf(stderr, "All send attempts failed.\n");
                fprintf(fp_log, "All send attempts failed.\n");
                fflush(fp_log);
            }
        }
        else if (ret != len) {
            fprintf(stderr, "Partial send: sent %zd/%d bytes\n", ret, len);
            fprintf(fp_log, "Partial send: sent %zd/%d bytes\n", ret, len);
            fflush(fp_log);
        }
        else {
            fprintf(stderr, "sendto success: %s\n", msg);
            fprintf(fp_log, "sendto success: %s\n", msg);
            fflush(fp_log);
            break; // 成功发送，退出循环
        }
    }

    close(sock);
}
static void send_done_message_ps(void) {
    fprintf(stderr, "udp%s", g_done_str);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket for done_msg");
        return;
    }

    struct sockaddr_in dst = { 0 };
    dst.sin_family = AF_INET;
    dst.sin_port = htons(g_cfg.ps_port);
    if (inet_aton(g_cfg.ps_ip, &dst.sin_addr) == 0) {
        fprintf(stderr, "invalid ps_ip: %s\n", g_cfg.ps_ip);
        close(sock);
        return;
    }

    char msg[64];
    int len = snprintf(msg, sizeof(msg),
        "get_frame_290_ps:done=%s",
        g_done_str);
    if (len < 0 || len >= (int)sizeof(msg)) {
        close(sock);
        return;
    }

    const int max_retries = 3;        // 最多重试次数
    const int retry_delay_ms = 200;   // 重试间隔（毫秒）

    int attempt;
    for (attempt = 1; attempt <= max_retries; attempt++) {
        ssize_t ret = sendto(sock, msg, len, 0,
            (struct sockaddr*)&dst, sizeof(dst));
        if (ret < 0) {
            perror("sendto failed");
            if (attempt < max_retries) {
                fprintf(stderr, "Retrying send (%d/%d)...\n", attempt, max_retries);
                fprintf(fp_log, "Retrying send (%d/%d)...\n", attempt, max_retries);
                fflush(fp_log);
                struct timespec ts;
                ts.tv_sec = retry_delay_ms / 1000;
                ts.tv_nsec = (retry_delay_ms % 1000) * 1000000L;
                nanosleep(&ts, NULL);
                continue;
            }
            else {
                fprintf(stderr, "All send attempts failed.\n");
                fprintf(fp_log, "All send attempts failed.\n");
                fflush(fp_log);
            }
        }
        else if (ret != len) {
            fprintf(stderr, "sendto partial send: %zd/%d bytes\n", ret, len);
            fprintf(fp_log, "sendto partial send: %zd/%d bytes\n", ret, len);
            fflush(fp_log);
        }
        else {
            fprintf(stderr, "sendto success: %s\n", msg);
            fprintf(fp_log, "sendto success: %s\n", msg);
            fflush(fp_log);
            break; // 成功发送，退出重试
        }
    }

    fprintf(stderr, "get_frame_290_ps:done=%s", g_done_str);
    fprintf(fp_log, "get_frame_290_ps:done=%s", g_done_str);
    fflush(fp_log);
    close(sock);
}



int main( int argc, char *argv[] ) {


    //    // 在最开始，先读取配置文件；若失败，则打印并退出
    // if (read_config("/mnt/ics.ini", &g_cfg) != 0) {
    //     fprintf(stderr, "Failed to read config ics.ini: %s\n",
    //         strerror(errno));
    //     return 1;
        
    // }
    strcpy(g_cfg.ics_ip, "192.168.0.233");
    strcpy(g_cfg.ps_ip, "192.168.0.100");

    g_cfg.ics_port = 11001;
    g_cfg.ps_port = 10001;



    if( argc < 3 ){
        printf("\n*********************************************\n");
        printf("usage: %s myName logFilename.txt\n", argv[0], argv[1]);
        printf("*********************************************\n");
        return 0;
    }
    
    strncpy(g_done_str, argv[1], sizeof(g_done_str)-1);

    sprintf(myName, "%s", argv[1]);
    fprintf(stdout, "# My Name is : %s\n", myName);

    sprintf(logFilename, "data/bin_tmp/%s_log.txt", argv[2]);
    fprintf(stdout, "# log filename: %s\n", logFilename);

    fp_log = fopen(logFilename, "wa");

    sprintf(binFilename, "data/bin_tmp/%s.bin", argv[1]);
    //    sprintf(binFilename, "/tmp/%s.bin", argv[1]);  // 测试写入用内存模拟的磁盘里

    int fd_gpio = open("/dev/mem", O_RDWR | O_SYNC);

    uint32_t *gpio_flt_ctrl     = mmap(NULL, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd_gpio, ADDR_Float_Point_CTRL);
    uint32_t *gpio_flt_data     = gpio_flt_ctrl + 2;

    uint32_t *gpio_in           = mmap(NULL, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd_gpio, ADDR_GPIO_IN);
    uint32_t *gpio_pl_ps        = gpio_in + 2;

    uint32_t *gpio_out          = mmap(NULL, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd_gpio, ADDR_GPIO_OUT);
    uint32_t *gpio_out2         = gpio_out + 2;

    uint32_t *gpio_spi_ps_ctrl  = mmap(NULL, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd_gpio, ADDR_GPIO_SPI_PS_CTRL);
    uint32_t *gpio_spi_ps_data  = gpio_spi_ps_ctrl + 2;

    uint32_t n_row      = 4700;
    uint32_t n_col      = 1250;
    uint32_t n_bytes    = 2;

    while(1) {

        // if( getbit(*gpio_in, 13) ){
        if( getbit(*gpio_in, 14) ){
            
            gen_timestamp(timestamp);
            fprintf(stderr, "[%s]> captured PL readout trigger signal\n", timestamp);
            fprintf(fp_log, "[%s]> captured PL readout trigger signal\n", timestamp);
            fflush(fp_log);

            ccd_readout_fpXX( gpio_out, 
                             gpio_in, 
                             n_row,
                             n_col,
                             n_bytes,    // 2 for 16bits unsigned; 4 for 32bits float
                             binFilename );

            printf("> saved %s\n", binFilename);
            fprintf(fp_log, "> saved %s\n", binFilename);
            fflush(fp_log);
        }

//        usleep(1000);   // sleep 1ms
        usleep(20);   // sleep 10us @20250806
//        usleep(100);   // sleep 100us @20250807
    }

    return 0;
}


int ccd_readout_fpXX( uint32_t *gpio_reg_ctrl, 
                     uint32_t *gpio_reg_status, 
                     uint32_t n_row,
                     uint32_t n_col,
                     uint32_t n_bytes,    // 2 for 16bits unsigned; 4 for 32bits float
                     char *filename ){

    gen_timestamp(timestamp);
    fprintf(stderr, "[%s]> enetring : %s\n", timestamp, __func__);
    fprintf(fp_log, "[%s]> enetring : %s\n", timestamp, __func__);
    fflush(fp_log);

    uint32_t base_addr = 0x20000000;    // 512MByte 特殊地址值！！！！
    int fd = open("/dev/mem", O_RDWR | O_SYNC);

#if defined(_ENABLE_FASTER_READOUT_)
//  @20240818: New and faster data storage strategy

    uint32_t mem_size_in_Bytes = n_col*16*n_bytes;   // the actual number of Bytes of a row
    uint32_t *mem_ptr = mmap(NULL, n_row*n_col*16*n_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, base_addr);
    uint32_t *ptr = mem_ptr;

    FILE *fb = NULL;
    fb = fopen(filename, "wba");   // append mode

    struct timeval start;
    struct timeval end;
    gettimeofday(&start, NULL);

/*    uint32_t abort = getbit*/
    uint32_t row_cnt = 0;
    
/*    sleep(10);*/
/*    usleep(rand() % 250000);*/
    
    gen_timestamp(timestamp);
    fprintf(stderr, "[%s]> start transferring data from DDR to binfile\n", timestamp);
    fprintf(fp_log, "[%s]> start transferring data from DDR to binfile\n", timestamp);
    fflush(fp_log);
    while(1){
        
        if( getbit(*gpio_reg_status, BIT_DDR_DATA_RDY) ){
            fwrite(ptr, sizeof(unsigned char), mem_size_in_Bytes, fb);
            ptr += n_col*n_bytes*4;
            row_cnt++;
//            fprintf(stdout, "row_cnt : %d\n", row_cnt);
        }

        if( row_cnt >= n_row )
            break;
    }

    gen_timestamp(timestamp);
    fprintf(stderr, "[%s]> finished transferring data from DDR to binfile\n", timestamp);
    fprintf(fp_log, "[%s]> finished transferring data from DDR to binfile\n", timestamp);
    fflush(fp_log);
    gettimeofday(&end, NULL);
    fprintf(stdout, "FPGA readout used time: %10.6f s\n", time_diff(&start, &end));

    //fflush(fb);

    if( fb != NULL )
        fclose(fb);
        
    uint32_t rd_is_done = getbit( *gpio_reg_status, BIT_READOUT_DONE );
    
    while( rd_is_done != 1 ){   // check whether readout is over
         rd_is_done = getbit(*gpio_reg_status, BIT_READOUT_DONE);
         if( rd_is_done == 1 ){
            printf("[CCD_READOUT] --> finish!\n");
            break;
        }
        usleep(1000);
    }

//    usleep(50000);
//    usleep(50);

    send_done_message();
    gen_timestamp(timestamp);
    fprintf(stderr, "[%s]> sent get_frame_done to ICS\n", timestamp);
    fprintf(fp_log, "[%s]> sent get_frame_done to ICS\n", timestamp);
    fflush(fp_log);

    send_done_message_ps();
    gen_timestamp(timestamp);
    fprintf(stderr, "[%s]> sent get_frame_done to Master PS\n", timestamp);
    fprintf(fp_log, "[%s]> sent get_frame_done to Master PS\n", timestamp);
    fflush(fp_log);
/*    char bin_ok_flag[256];*/
/*    sprintf(bin_ok_flag, "%s_ok", filename);*/
/*    FILE *fp_ok = fopen(bin_ok_flag, "w");*/
/*    fprintf(fp_ok, "1");*/
/*    fclose(fp_ok);*/

    munmap(mem_ptr, n_row*n_col*16*n_bytes);

#else
//  ===========================================================================================
//  **** Old and slow data storage strategy ****
    uint32_t mem_size_in_Bytes_X = n_col*n_row*16*n_bytes;   // the actual size
    uint32_t *mem_ptr = mmap(NULL, mem_size_in_Bytes_X, PROT_READ | PROT_WRITE, MAP_SHARED, fd, base_addr);

    setbit( *gpio_reg_ctrl, BIT_PS_DATA_SAVED );
    
    time_t t_start, t_end;
    time(&t_start);
    uint32_t rd_is_done = getbit( *gpio_reg_status, BIT_READOUT_DONE );
    printf("[CCD_READOUT] --> start\n");
    
    while( rd_is_done != 1 ){   // check whether readout is over
         rd_is_done = getbit(*gpio_reg_status, BIT_READOUT_DONE);
         if( rd_is_done == 1 ){
            printf("[CCD_READOUT] --> finish!\n");
            break;
        }
        usleep(1000);
    }

    FILE *fb = NULL;
    fb = fopen(filename, "wb");
    printf("[CCD_READOUT] --> start transferring data from DDR to disk ...\n");
    fwrite((void *)mem_ptr, sizeof(unsigned char), mem_size_in_Bytes_X, fb);


    time(&t_end);
    printf("[CCD_READOUT] --> total time used %ld seconds.\n", t_end - t_start);

    if( fb != NULL )
        fclose(fb);
    
    send_done_message();
    send_done_message_ps();
    
    // char bin_ok_flag[256];
    // sprintf(bin_ok_flag, "%s_ok", filename);
    // FILE *fp_ok = fopen(bin_ok_flag, "w");
    // fprintf(fp_ok, "1");
    // fclose(fp_ok);

    memset(mem_ptr, rand()%100, n_col*n_row*16);

    // munmap(mem_ptr, mem_size_in_Bytes_X);
#endif

    close(fd);
    DropCaches(3);    //  清空缓存
    
    gen_timestamp(timestamp);
    fprintf(stderr, "[%s]> leaving : %s\n", timestamp, __func__);
    fprintf(fp_log, "[%s]> leaving : %s\n", timestamp, __func__);
    fflush(fp_log);

    return 0;
}

